package main

import (
	"encoding/json"
	"math"
	"math/rand"
	"strconv"

	"github.com/golang/glog"
)

type Message struct {
	Client *Client
	MType  int
	Data   []byte
}

type Hub struct {
	message    chan Message
	register   chan *Client
	unregister chan *Client
	clients    map[*Client]bool
}

func NewHub() *Hub {
	return &Hub{
		message:    make(chan Message),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}
}

func (h *Hub) genClientID() string {
	flag := true
	id := ""
	for flag {
		flag = false
		r := int64(math.Pow(float64(36), float64(4)) + float64(rand.Int31n(int32(math.Pow(float64(36), float64(4))-math.Pow(float64(36), float64(3))))))
		id = strconv.FormatInt(int64(r), 36)

		for c, _ := range h.clients {
			if c.id == id {
				flag = true
			}
		}
	}

	return id
}

func (h *Hub) run() {
	for {
		select {
		case client := <-h.register:
			client.id = h.genClientID()
			h.clients[client] = true
			h.SendPack2Client(client, &Pack{Type: "Register", Data: Register{ID: client.id}})
		case client := <-h.unregister:
			h.removeClient(client)
		case message := <-h.message:
			h.handleMessage(message)
		}
	}
}

func (h *Hub) handleMessage(message Message) {
	pack := Pack{}
	if err := json.Unmarshal(message.Data, &pack); err != nil {
		glog.Info(err)
		h.removeClient(message.Client)
	}

	if pack.To == "" {
		switch pack.Type {
		case "ClientID":

		}
	} else {
		existed := false
		for c, v := range h.clients {

			if v && c.id != pack.To {
				continue
			}

			existed = true
			pack.From = message.Client.id
			h.SendPack2Client(c, &pack)
			break
		}

		if !existed {
			h.SendPack2Client(message.Client, &Pack{Type: "no-remote-id"})
		}
	}

}

func (h *Hub) SendBytes2Client(client *Client, data []byte) {
	select {
	case client.send <- data:
	default:
		h.removeClient(client)
	}
}

func (h *Hub) SendPack2Client(client *Client, pkg *Pack) {
	p, err := json.Marshal(*pkg)
	if err != nil {
		glog.Fatal(err)
	}

	h.SendBytes2Client(client, p)
}

func (h *Hub) removeClient(c *Client) {
	if _, ok := h.clients[c]; ok {
		delete(h.clients, c)
		close(c.send)
	}
}

type Pack struct {
	Type string
	To   string
	From string
	Data interface{}
}

type Register struct {
	ID string
}
